﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Security.Cryptography;
using System.ComponentModel;

namespace CSSAdriverapp
{
    /// <summary>
    /// Interaction logic for ScheduleTransportation.xaml
    /// </summary>
    public partial class ScheduleTransportation : Window
    {
         int count = new int();
        BackgroundWorker worker = new BackgroundWorker();
        List<DateTime> dates = new List<DateTime>();

        public ScheduleTransportation()
        {
            InitializeComponent();
            termDisplayLabel.Content = getTerm();

        }

        private void assignVehiclesCalc(){

            //deletePastAssignments();
            DataClassesDataContext db = new DataClassesDataContext();


            //assigning-------------------------------------------
            IQueryable<Event> events = from c in db.Events where dates.Contains(c.Date) select c;
            IQueryable<COMSERV_ACTIVITY> activities = from c in db.COMSERV_ACTIVITies
                                                      where c.semester_yearid == getTermID()
                                                      select c;
            IQueryable<COMSERV_DATE_EXCLUDED> excluded = from c in db.COMSERV_DATE_EXCLUDEDs select c;
            IQueryable<Vehicle> vehics = from c in db.Vehicles select c;
            List<Vehicle> vehicles = vehics.ToList();
            events.ToList();
            activities.ToList();
            excluded.ToList();

            DateTime start = new DateTime();
            DateTime end = new DateTime();
            DateTime date = new DateTime();
            int prev = 0;
            count = events.Count(); //for progress bar

            try{
            foreach (Event c in events)
            {
                COMSERV_ACTIVITY activity = (from d in activities
                                             where d.activityid == (int)c.ServiceID
                                             select d).FirstOrDefault();

                if(!(activity.needed == "Weekly" || activity.needed == "BiWeekly")){
                COMSERV_DATE_EXCLUDED excluding = (from e in excluded where e.date == c.Date select e).FirstOrDefault();

                if (prev != c.ServiceID && prev != 0)
                {
                    vehicles = mixVehicles(vehicles);//mixes vehicles
                }

                if (activity.transportation != null && activity.transportation != 0)// makes sure it needs trans
                {
                    if (excluding == null)// only if its not excluded
                    {
                        start = (DateTime)activity.begin_time;
                        end = (DateTime)activity.end_time;
                        date = c.Date;

                        if (activity.travel_time != null)
                        {
                            start = start.AddMinutes(-(int)activity.travel_time);
                            end = end.AddMinutes((int)activity.travel_time);
                        }

                        foreach (Vehicle v in vehicles)
                        {
                            if (enoughSeats((int)v.VehicleTypeID,activity))//only see if its available if it has enough seats
                            {
                                if (isAvailable(v, start, end, date))
                                {
                                    assignVehicle(c, v);
                                    break;
                                }
                            }
                        }
                    }
                    else
                    {
                        assignExcluded(c, excluding.comments);
                    }
                }
                else
                {
                    assignWalk(c);
                }
                worker.ReportProgress(1);
                prev = c.ServiceID;
            }
            }
            }
            catch(Exception e)
            {
                Console.WriteLine(e);
            }
        }

        private void assignVehicle(Event e, Vehicle v)
        {
            DataClassesDataContext db = new DataClassesDataContext();
            // Query the database for the row to be updated.
            var query =
                from ev in db.Events
                where ev.ID == e.ID
                select ev;
            // Execute the query, and change the column values
            // you want to change.
            foreach (Event ev in query)
            {
                ev.VehicleID = v.ID;
                // Insert any additional changes to column values.
            }

            // Submit the changes to the database.
            try
            {
                db.SubmitChanges();
            }
            catch (Exception w)
            {
                Console.WriteLine(w);
                // Provide for exceptions.
            }
        }

        private void assignWalk(Event e)
        {
            DataClassesDataContext db = new DataClassesDataContext();
            // Query the database for the row to be updated.
            var query =
                from ev in db.Events
                where ev.ID == e.ID
                select ev;
            // Execute the query, and change the column values
            // you want to change.
            foreach (Event ev in query)
            {
                ev.VehicleID = 1;
                // Insert any additional changes to column values.
            }

            // Submit the changes to the database.
            try
            {
                db.SubmitChanges();
            }
            catch (Exception w)
            {
                Console.WriteLine(w);
                // Provide for exceptions.
            }
        }

        private void assignExcluded(Event e, String comment)
        {
            DataClassesDataContext db = new DataClassesDataContext();
            // Query the database for the row to be updated.
            var query =
                from ev in db.Events
                where ev.ID == e.ID
                select ev;
            // Execute the query, and change the column values
            // you want to change.
            foreach (Event ev in query)
            {
                ev.Notes = comment;
                // Insert any additional changes to column values.
            }

            // Submit the changes to the database.
            try
            {
                db.SubmitChanges();
            }
            catch (Exception w)
            {
                Console.WriteLine(w);
                // Provide for exceptions.
            }
        }

        private static Boolean isAvailable(Vehicle vehicle, DateTime start, DateTime end, DateTime date)
        {        
              if (vehicle.Unavailable != true)
              {
                if (haveIt(vehicle,start,end,date))//see if we even have it
                {
                    if (isntOut(vehicle, start, end, date)) // see if its out
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        private static Boolean haveIt(Vehicle vehicle, DateTime start, DateTime end, DateTime date)//checks if we have the vehicle at that time
        {
            DataClassesDataContext db = new DataClassesDataContext();
            IQueryable<VehicleAvailability> availability = from c in db.VehicleAvailabilities
                                                           where c.vehicleID == vehicle.ID
                                                           select c;
            availability.ToList();

            foreach (VehicleAvailability c in availability)
            {              
                if (start.TimeOfDay >= c.starttime.TimeOfDay)
                {
                    if (end.TimeOfDay <= c.endtime.TimeOfDay)
                    {
                        return true;
                    }
                } 
            }
            return false;
        }

        private static Boolean isntOut(Vehicle vehicle, DateTime start, DateTime end, DateTime date)
        {
            DataClassesDataContext db = new DataClassesDataContext();

            IQueryable<Event> events = from c in db.Events
                                       where c.Date.Date == date.Date && vehicle.ID == c.VehicleID
                                       select c;
            events.ToList();
            if (events.Count() == 0)//if it has no events that day it passes
            {
                return true;
            }
            else
            {
                foreach(Event e in events){
                    COMSERV_ACTIVITY activity = (from c in db.COMSERV_ACTIVITies
                                                where c.activityid == e.ServiceID
                                                select c).FirstOrDefault();

                    DateTime ab = (DateTime)activity.begin_time;
                    DateTime ae = (DateTime)activity.end_time;

                    if (activity.travel_time != null)
                    {
                        ab = ab.AddMinutes(-(int)activity.travel_time);
                        ae = ae.AddMinutes((int)activity.travel_time);
                    } 

                    if ((start.TimeOfDay < ab.TimeOfDay && end.TimeOfDay < ab.TimeOfDay) || (start.TimeOfDay > ae.TimeOfDay && end.TimeOfDay > ae.TimeOfDay))
                    {

                    }
                    else
                    {
                        return false; // if confliction occurs
                    }
                     //if none of the activities conflict
                }
                return true;
            }
        }

        private static List<Vehicle> sortVehicles(List<Vehicle> tvehicles)
        {
            try
            {
                DataClassesDataContext db = new DataClassesDataContext();
                //get types
                IQueryable<VehicleType> types = from c in db.VehicleTypes orderby c.NumOfSeats ascending select c;

                types.ToList();

                List<Vehicle> vehicles = new List<Vehicle>();

                //sort
                foreach (VehicleType t in types)
                {
                    foreach (Vehicle v in tvehicles)
                    {
                        if (v.VehicleTypeID == t.ID)
                        {
                            vehicles.Add(v);
                        }
                    }
                }

                return vehicles;
            }

            catch(Exception e)
            {
                Console.WriteLine(e);
            }

            return null;
        }

        private static string getTerm()
        {
            DataClassesDataContext db = new DataClassesDataContext();

            int term = getTermID();

            if (term != 0)
            {
               
                IQueryable<COMSERV_SEMESTER_YEAR> info = from c in db.COMSERV_SEMESTER_YEARs 
                                                         where c.semester_yearid == (int)term
                                                         select c;
                info.ToList();
                return info.First().semester + " " + info.First().year;
            }
            return "Please generate events first!";
        }

        private static int getTermID()
        {
            DataClassesDataContext db = new DataClassesDataContext();

            try
            {
                int id = (int)(from e in db.Events
                               join a in db.COMSERV_ACTIVITies on (int)e.ServiceID equals (int)a.activityid
                               select a.semester_yearid).FirstOrDefault();
                return id;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            return 0;                      
        }

        private static COMSERV_ACTIVITY getActivity(int id)
        {
            try
            {
                DataClassesDataContext db = new DataClassesDataContext();
                COMSERV_ACTIVITY activity = (from c in db.COMSERV_ACTIVITies
                                             where c.activityid == (int)id
                                             select c).FirstOrDefault();
                return activity;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            return null;
        }

        private static Boolean enoughSeats(int id, COMSERV_ACTIVITY activity)
        {
            DataClassesDataContext db = new DataClassesDataContext();

            int num = (int)(from c in db.VehicleTypes where c.ID == id select c.NumOfSeats).FirstOrDefault();

            if (activity.transnumb <= num)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private static List<Vehicle> mixVehicles(List<Vehicle> vehicles)
        {
            RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
            int n = vehicles.Count;
            while (n > 1)
            {
                byte[] box = new byte[1];
                do provider.GetBytes(box);
                while (!(box[0] < n * (Byte.MaxValue / n)));
                int k = (box[0] % n);
                n--;
                Vehicle value = vehicles[k];
                vehicles[k] = vehicles[n];
                vehicles[n] = value;
            } 
            return sortVehicles(vehicles);
        }

        private void deletePastAssignments()
        {
            DataClassesDataContext db = new DataClassesDataContext();

            // Query the database for the row to be updated.
            var query =
                from e in db.Events
               // where e.OrderID == 11000
                select e;

            // Execute the query, and change the column values
            // you want to change.
            foreach (Event e in query)
            {
                e.VehicleID = null;
                e.Notes = null;
                // Insert any additional changes to column values.
            }

            // Submit the changes to the database.
            try
            {
                db.SubmitChanges();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                // Provide for exceptions.
            }
        }

        private void scheduleButton_Click(object sender, RoutedEventArgs e)
        {
            
            dates = calendar.SelectedDates.ToList();
                //Message box code
            if (dates.Count() > 0)
            {
                string message = "Are you sure? This action will delete all past data. Do not close window while process is running.";
                string caption = "Confirmation";
                MessageBoxResult result = MessageBox.Show(message, caption,
                                         MessageBoxButton.YesNo,
                                         MessageBoxImage.Question);
                if (result == MessageBoxResult.Yes)
                {
                    label3.Visibility = System.Windows.Visibility.Visible;
                    scheduleButton.Visibility = System.Windows.Visibility.Hidden;
                    StartWork();//Start doing the long update
                }
            }
        }

        private void StartWork()
        {
            progressBar.Visibility = System.Windows.Visibility.Visible;
            worker.DoWork += DoWork;
            worker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
            worker.WorkerReportsProgress = true;
            worker.RunWorkerCompleted += WorkerCompleted;
            worker.RunWorkerAsync();
        }

        private void DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                assignVehiclesCalc();//start and hope for the best
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex);
                //label1.Content = "Error";
            }
        }

        private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //Hide your wait dialog
            this.Close();
        }

        void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // The progress percentage is a property of e
            progressBar.Maximum = count;
            progressBar.Value++;
        }



    }
    }


